import {Matrix4} from '../math/Matrix4.js'
import {Vector2} from '../math/Vector2.js'
import {Vector3} from '../math/Vector3.js'
import {Vector4} from '../math/Vector4.js'
import {Frustum} from '../math/Frustum.js'

function LightShadow(camera) {
  this.camera = camera

  this.bias = 0
  this.normalBias = 0
  this.radius = 1

  this.mapSize = new Vector2(512, 512)

  this.map = null
  this.mapPass = null
  this.matrix = new Matrix4()

  this.autoUpdate = true
  this.needsUpdate = false

  this._frustum = new Frustum()
  this._frameExtents = new Vector2(1, 1)

  this._viewportCount = 1

  this._viewports = [new Vector4(0, 0, 1, 1)]
}

Object.assign(LightShadow.prototype, {
  _projScreenMatrix: new Matrix4(),

  _lightPositionWorld: new Vector3(),

  _lookTarget: new Vector3(),

  getViewportCount: function () {
    return this._viewportCount
  },

  getFrustum: function () {
    return this._frustum
  },

  updateMatrices: function (light) {
    const shadowCamera = this.camera,
      shadowMatrix = this.matrix,
      projScreenMatrix = this._projScreenMatrix,
      lookTarget = this._lookTarget,
      lightPositionWorld = this._lightPositionWorld

    lightPositionWorld.setFromMatrixPosition(light.matrixWorld)
    shadowCamera.position.copy(lightPositionWorld)

    lookTarget.setFromMatrixPosition(light.target.matrixWorld)
    shadowCamera.lookAt(lookTarget)
    shadowCamera.updateMatrixWorld()

    projScreenMatrix.multiplyMatrices(shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse)
    this._frustum.setFromProjectionMatrix(projScreenMatrix)

    shadowMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0)

    shadowMatrix.multiply(shadowCamera.projectionMatrix)
    shadowMatrix.multiply(shadowCamera.matrixWorldInverse)
  },

  getViewport: function (viewportIndex) {
    return this._viewports[viewportIndex]
  },

  getFrameExtents: function () {
    return this._frameExtents
  },

  copy: function (source) {
    this.camera = source.camera.clone()

    this.bias = source.bias
    this.radius = source.radius

    this.mapSize.copy(source.mapSize)

    return this
  },

  clone: function () {
    return new this.constructor().copy(this)
  },

  toJSON: function () {
    const object = {}

    if (this.bias !== 0) object.bias = this.bias
    if (this.normalBias !== 0) object.normalBias = this.normalBias
    if (this.radius !== 1) object.radius = this.radius
    if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray()

    object.camera = this.camera.toJSON(false).object
    delete object.camera.matrix

    return object
  },
})

export {LightShadow}
